// 
// Copyright (c) 2006-2009 Ben Motmans
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Author(s):
//    Ben Motmans <ben.motmans@gmail.com>
//

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace Anculus.Core
{
	//TODO: save method, with includePrivateKey
	public sealed class RsaKey
	{
		public RsaKey (int bitsize)
		{
			if ((bitsize & (bitsize - 1)) != 0 || bitsize < 512)
				throw new ArgumentException ("bitsize must be a power of 2 and >= 512.");
			
			CryptoProvider = new RSACryptoServiceProvider (bitsize);
		}
		
		public RsaKey ()
			: this (1024)
		{
		}
		
		private RsaKey (RSACryptoServiceProvider cryptoProvider)
		{
			CryptoProvider = cryptoProvider;
		}

		public RSACryptoServiceProvider CryptoProvider { get; protected set; }

		public int KeySize
		{
			get { return CryptoProvider.KeySize; }
		}
		
		//TODO: method which allows to specify the format (pem, asn1, ...)
		public static RsaKey FromFile (string fileName)
		{
			if (string.IsNullOrEmpty (fileName))
				throw new ArgumentException ("fileName");
			
			string ext = Path.GetExtension (fileName);
			switch (ext) {
			case ".pem":
				return new RsaKey (OpenSslRsaKey.FromPemFile (fileName));
			case ".xml":
				string xml = null;
				using (StreamReader reader = File.OpenText (fileName)) {
					xml = reader.ReadToEnd ();
				}
				RSACryptoServiceProvider rsa = new RSACryptoServiceProvider ();
				rsa.FromXmlString (xml);
				
				return new RsaKey (rsa);
			default:
				throw new NotSupportedException (String.Format ("'{0}' is not a valid key format, please specify a .pem or .xml file"));
			}
		}

		public void SaveToFile (string fileName, bool includePrikey)
		{
			if (string.IsNullOrEmpty (fileName))
				throw new ArgumentException ("fileName");
			
			string ext = Path.GetExtension (fileName);
			switch (ext) {
			case ".pem":
				//TODO: implement
				throw new NotImplementedException ();
			case ".xml":
				string xml = CryptoProvider.ToXmlString (includePrikey);
				using (FileStream fs = new FileStream (fileName, FileMode.Truncate, FileAccess.Write)) {
					using (StreamWriter writer = new StreamWriter (fs, Encoding.UTF8)) {
						writer.Write (xml);
					}
				}
				break;
			default:
				throw new NotSupportedException (String.Format ("'{0}' is not a valid key format, please specify a .pem or .xml file"));
			}
		}
		
		public void SaveToFile (string fileName)
		{
			SaveToFile (fileName, false);
		}
	}
}